package com.gitee.wsl.platform.network

import java.net.*
import java.util.*

object HostInterface {
    /**///////////////////////////////////////////// */ //	Constants
    /**///////////////////////////////////////////// */
    var USE_LOOPBACK_ADDR: Boolean = false
    var USE_ONLY_IPV4_ADDR: Boolean = false
    var USE_ONLY_IPV6_ADDR: Boolean = false

    /**///////////////////////////////////////////// */ //	Network Interfaces
    /**///////////////////////////////////////////// */
    var `interface`: String = ""
    const val IPV4_BITMASK: Int = 0x0001
    const val IPV6_BITMASK: Int = 0x0010
    const val LOCAL_BITMASK: Int = 0x0100

    private fun hasAssignedInterface(): Boolean {
        return if (`interface`.isNotEmpty()) true else false
    }

    /**///////////////////////////////////////////// */ //	Network Interfaces
    /**///////////////////////////////////////////// */ // Thanks for Theo Beisch (10/27/04)
    private fun isUsableAddress(addr: InetAddress): Boolean {
        if (!USE_LOOPBACK_ADDR) {
            if (addr.isLoopbackAddress() == true) return false
        }
        if (USE_ONLY_IPV4_ADDR) {
            if (addr is Inet6Address) return false
        }
        if (USE_ONLY_IPV6_ADDR) {
            if (addr is Inet4Address) return false
        }
        return true
    }

    val nHostAddresses: Int
        get() {
            if (hasAssignedInterface() == true) return 1

            var nHostAddrs = 0
            try {
                val nis: Enumeration<*> = NetworkInterface.getNetworkInterfaces()
                while (nis.hasMoreElements()) {
                    val ni = nis.nextElement() as NetworkInterface
                    val addrs: Enumeration<*> = ni.getInetAddresses()
                    while (addrs.hasMoreElements()) {
                        val addr = addrs.nextElement() as InetAddress
                        if (isUsableAddress(addr) == false) continue
                        nHostAddrs++
                    }
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }

            return nHostAddrs
        }

    /**
     * @param ipfilter
     * @param interfaces
     * @return
     * @since 1.8.0
     * @author Stefano "Kismet" Lenzi &lt;kismet.sl@gmail.com&gt;
     */
    fun getInetAddress(ipfilter: Int, interfaces: Array<String>?): List<InetAddress> {
        val nis: Iterator<NetworkInterface>
        if (interfaces != null) {
            val iflist= mutableListOf<NetworkInterface>()
            for (i in interfaces.indices) {
                val ni: NetworkInterface?
                try {
                    ni = NetworkInterface.getByName(interfaces[i])
                } catch (e: SocketException) {
                    continue
                }
                if (ni != null) iflist.add(ni)
            }
            nis = iflist.iterator()
        } else {
            try {
                nis = NetworkInterface.getNetworkInterfaces().asIterator()
            } catch (e: SocketException) {
                return emptyList()
            }
        }
        val addresses = ArrayList<InetAddress>()
        while (nis.hasNext()) {
            val ni = nis.next()
            val addrs = ni.inetAddresses
            while (addrs.hasMoreElements()) {
                val addr = addrs.nextElement()
                if (((ipfilter and LOCAL_BITMASK) == 0) && addr.isLoopbackAddress)
                    continue
                if (((ipfilter and IPV4_BITMASK) != 0) && addr is Inet4Address) {
                    addresses.add(addr)
                } else if (((ipfilter and IPV6_BITMASK) != 0) && addr is InetAddress) {
                    addresses.add(addr)
                }
            }
        }
        return addresses
    }

    fun getHostAddress(n: Int): String {
        if (hasAssignedInterface()) return `interface`

        var hostAddrCnt = 0
        try {
            val nis = NetworkInterface.getNetworkInterfaces()
            while (nis.hasMoreElements()) {
                val ni = nis.nextElement() as NetworkInterface
                val addrs = ni.inetAddresses
                while (addrs.hasMoreElements()) {
                    val addr = addrs.nextElement() as InetAddress
                    if (!isUsableAddress(addr)) continue
                    if (hostAddrCnt < n) {
                        hostAddrCnt++
                        continue
                    }
                    val host = addr.hostAddress
                    // if (addr instanceof Inet6Address)
                    //	host = "[" + host + "]";
                    return host
                }
            }
        } catch (e: Exception) {
        }
        return ""
    }

    /**///////////////////////////////////////////// */ //	isIPv?Address
    /**///////////////////////////////////////////// */
    fun isIPv6Address(host: String): Boolean {
        try {
            val addr = InetAddress.getByName(host)
            if (addr is Inet6Address) return true
            return false
        } catch (e: Exception) {
        }
        return false
    }

    fun isIPv4Address(host: String): Boolean {
        try {
            val addr = InetAddress.getByName(host)
            if (addr is Inet4Address) return true
            return false
        } catch (e: Exception) {
        }
        return false
    }

    /**///////////////////////////////////////////// */ //	hasIPv?Interfaces
    /**///////////////////////////////////////////// */
    fun hasIPv4Addresses(): Boolean {
        val addrCnt = nHostAddresses
        for (n in 0..<addrCnt) {
            val addr = getHostAddress(n)
            if (isIPv4Address(addr)) return true
        }
        return false
    }

    fun hasIPv6Addresses(): Boolean {
        val addrCnt = nHostAddresses
        for (n in 0..<addrCnt) {
            val addr = getHostAddress(n)
            if (isIPv6Address(addr)) return true
        }
        return false
    }

    val iPv4Address: String
        /**/
        get() {
            val addrCnt = nHostAddresses
            for (n in 0..<addrCnt) {
                val addr = getHostAddress(n)
                if (isIPv4Address(addr)) return addr
            }
            return ""
        }

    val iPv6Address: String
        get() {
            val addrCnt = nHostAddresses
            for (n in 0..<addrCnt) {
                val addr = getHostAddress(n)
                if (isIPv6Address(addr)) return addr
            }
            return ""
        }

    /**///////////////////////////////////////////// */ //	getHostURL
    /**///////////////////////////////////////////// */
    fun getHostURL(host: String, port: Int, uri: String): String {
        var hostAddr = host
        if (isIPv6Address(host)) hostAddr = "[$host]"
        return "http://$hostAddr:$port$uri"
    }
}
